package com.stars.easyms.rest.handler;

import com.stars.easyms.base.alarm.EasyMsAlarmAssistor;
import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.bean.EasyMsRequestEntity;
import com.stars.easyms.base.trace.EasyMsTraceSynchronizationManager;
import com.stars.easyms.base.util.*;
import com.stars.easyms.monitor.register.EasyMsMonitorRegister;
import com.stars.easyms.rest.bean.EasyMsRestContext;
import com.stars.easyms.rest.properties.EasyMsRestProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.NonNull;

import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * <p>className: EasyMsResponseHandler</p>
 * <p>description: EasyMs的响应处理器</p>
 *
 * @author guoguifang
 * @version 1.6.2
 * @date 2020/9/5 3:26 下午
 */
@SuppressWarnings("unchecked")
public final class EasyMsResponseHandler {

    private static final Logger logger = LoggerFactory.getLogger(EasyMsResponseHandler.class);

    private final EasyMsRestProperties easyMsRestProperties;

    private final EasyMsRestResponseHandler easyMsRestResponseHandler;

    private HttpMessageConverter fastJsonHttpMessageConverter;

    private HttpMessageConverter mappingJackson2HttpMessageConverter;

    public EasyMsResponseHandler(EasyMsRestProperties easyMsRestProperties) {
        this.easyMsRestProperties = easyMsRestProperties;
        this.easyMsRestResponseHandler = new EasyMsRestResponseHandler();
        this.fastJsonHttpMessageConverter = FastJsonUtil.getFastJsonHttpMessageConverter();
        this.mappingJackson2HttpMessageConverter = ApplicationContextHolder.getBean(MappingJackson2HttpMessageConverter.class);
    }

    public void handleResponse(@NonNull EasyMsRestContext easyMsRestContext) {
        try {
            // 处理response的日志记录并返回响应
            handleResponseTrace(easyMsRestContext);
            writeResponse(easyMsRestContext);
        } finally {

            // 释放本地线程保存的rest相关信息
            clearRestSynchronization();
        }
    }

    private void handleResponseTrace(@NonNull EasyMsRestContext easyMsRestContext) {
        // 如果有异常先记录再告警
        Throwable throwable = easyMsRestContext.getThrowable();
        if (throwable != null) {
            EasyMsRestExceptionHandler.handleRestAlarmException(throwable);
        }

        // 获取参数信息
        EasyMsRequestEntity requestEntity = easyMsRestContext.getEasyMsRequestEntity();
        String traceId = requestEntity.getTraceId();
        String requestId = requestEntity.getRequestId();
        String asyncId = requestEntity.getAsyncId();
        String requestSys = requestEntity.getRequestSys();
        String responseSys = SpringBootUtil.getApplicationName();
        String requestPath = requestEntity.getRequestPath();

        // 获取返回响应时间
        long responseTime = System.currentTimeMillis();
        String responseTimeStr = DateTimeUtil.getDatetimeNormalStrWithMills(responseTime);
        long costTime = responseTime - requestEntity.getReceiveRequestTime();

        // 将参数放入响应头中
        HttpServletResponse response = easyMsRestContext.getHttpServletResponse();
        response.setHeader(HttpHeaderConstants.TRACE_KEY, traceId);
        response.setHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_ID, requestId);
        response.setHeader(HttpHeaderConstants.HEADER_KEY_ASYNC_ID, asyncId);
        response.setHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_SYS, requestSys);
        response.setHeader(HttpHeaderConstants.HEADER_KEY_RESPONSE_SYS, responseSys);
        response.setHeader(HttpHeaderConstants.HEADER_KEY_RESPONSE_TIME, responseTimeStr);
        if (easyMsRestContext.isEasyMsRest()) {
            response.setHeader(HttpHeaderConstants.HEADER_KEY_EASY_MS, "true");
        }

        // 先判断是否是@ResponseBody类型，如果是则获取responseEntity对象
        Object responseEntity = null;
        if (easyMsRestContext.isReturnResponseBody()) {
            responseEntity = easyMsRestContext.getResponseEntity();
            if (responseEntity == null) {
                easyMsRestResponseHandler.handleRestResponse(easyMsRestContext);
                responseEntity = easyMsRestContext.getResponseEntity();
            }
        }

        // 记录请求日志
        if (isNotMonitorUrl(requestPath)) {
            // 当配置文件设置打印响应信息并且rest上下文需要记录返回信息时、或者有异常信息返回时需要记录响应信息
            boolean isRecordResponse = responseEntity != null && easyMsRestProperties.isLogResponse() &&
                    (!easyMsRestContext.isNotRecordReturnData() || easyMsRestContext.isResponseBodyWithThrowable());
            logger.info("[接收服务-响应]-[请求地址: {}]-[请求系统: {}]-[服务系统: {}]-[请求ID: {}]{}-[响应时间: {}]-[用时: {}ms]{}.",
                    requestPath, requestSys, responseSys, requestId,
                    TraceUtil.getAsyncIdTrace(asyncId), responseTimeStr, costTime,
                    isRecordResponse ? "-响应数据: " + briefly(JsonUtil.toJSONString(responseEntity)) : "");
        }

        // 如果接口用时超过预警值则告警
        if (responseEntity != null && costTime >= easyMsRestProperties.getAlarmLimitTime()) {
            String warnMessage = MessageFormatUtil.format("接口[{}]用时较长，共{}ms，请优化接口！", requestPath, costTime);
            logger.warn(warnMessage);
            EasyMsAlarmAssistor.sendNormalAlarmMessage(warnMessage);
        }
    }

    private void writeResponse(EasyMsRestContext easyMsRestContext) {
        if (!easyMsRestContext.isEasyMsRest()) {
            return;
        }

        // 处理response
        ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(easyMsRestContext.getHttpServletResponse());
        outputMessage.getServletResponse().setStatus(HttpStatus.OK.value());
        Object obj = easyMsRestContext.getEncryptedResponseEntity() != null ?
                easyMsRestContext.getEncryptedResponseEntity() : easyMsRestContext.getResponseEntity();
        try {
            if (!easyMsRestProperties.isFastJson() && mappingJackson2HttpMessageConverter != null) {
                mappingJackson2HttpMessageConverter.write(obj, MediaType.APPLICATION_JSON, outputMessage);
            } else {
                fastJsonHttpMessageConverter.write(obj, MediaType.APPLICATION_JSON, outputMessage);
            }
            outputMessage.flush();
        } catch (IOException ex) {
            logger.error("响应信息write失败!", ex);
        }
    }

    private boolean isNotMonitorUrl(String url) {
        return !EasyMsMonitorRegister.getMonitorModuleNames().contains(url.substring(1));
    }

    private void clearRestSynchronization() {
        EasyMsRestSynchronizationManager.clear();
        EasyMsTraceSynchronizationManager.clear();
    }

    private String briefly(String str) {
        int strLength = str.length();
        if (strLength > 4200) {
            return str.substring(0, 2000) + "..." + str.substring(strLength - 2000);
        }
        return str;
    }

}
